package net.threeple.logger.data.enhancer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import com.google.common.base.CaseFormat;

import net.threeple.logger.data.annotation.Recordable;
import net.threeple.logger.data.annotation.WebRecorder;
import net.threeple.logger.data.exception.NoEnoughInformationException;
import net.threeple.logger.data.model.DataLog;

public abstract class WebRecorderAbstractAspect {
	static final Logger logger = LoggerFactory.getLogger(WebRecorderAbstractAspect.class);
	
	@After("@annotation(recorder)")
	public void record(JoinPoint joinPoint, WebRecorder recorder) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod(); 
		Class<?>[] paramTypes = method.getParameterTypes();
		Annotation[][] parameterAnnotations = method.getParameterAnnotations();
		Object[] args = joinPoint.getArgs();
		int i = 0;
		
		DataLog log = new DataLog();
		log.setChangeTime(new Date());
		String methodName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, method.getName());
		String operationType = methodName.toUpperCase();
		if(methodName.indexOf("_") > 0) {
			operationType = methodName.substring(0, methodName.indexOf("_"));
		}
		log.setOperationType(operationType);
		
		for(Annotation[] annotations : parameterAnnotations) {
			Class<?> paramType = paramTypes[i];
			Object arg = args[i];
			if(paramType.equals(HttpServletRequest.class)) {
				HttpServletRequest request = (HttpServletRequest) arg;
				Object obj = null;
				switch(recorder.fetchUserFrom()) {
				case REQUEST:
					obj = request.getAttribute(recorder.userInAttr());
					break;
				case SESSION:
					obj = request.getSession().getAttribute(recorder.userInAttr());
					break;
				}
				
				if(obj == null) {
					throw new NoEnoughInformationException("没有足够的信息可供记录数据变更，缺少用户信息");
				}
				BeanWrapper user = new BeanWrapperImpl(obj);
				log.setUserClazz(obj.getClass().getName());
				String userId = String.valueOf(user.getPropertyValue(recorder.userId()));
				log.setUserId(userId);
				
				log.setIp(getIPAddress(request));
			}
			i++;
			for(Annotation annotation : annotations) {
				if(annotation instanceof Recordable) {// 数据实体
					Recordable recordable = (Recordable) annotation;
					BeanWrapper data = new BeanWrapperImpl(arg);
					String payloadId = String.valueOf(data.getPropertyValue(recordable.id()));
					log.setPayloadId(payloadId);
					log.setPayloadClazz(paramType.getName());
					log.setPayload(arg);
				}
			}
		}
		saveLog(log); // 保存数据变更信息
	}
	
	private String getIPAddress(HttpServletRequest request) {
		String remoteAddr = request.getRemoteAddr();
		String realIp = request.getHeader("X-Real-IP");
		String forwarded = request.getHeader("X-Forwarded-For");
		String ip = null;
		if(realIp == null) {
			if (forwarded == null) {
                ip = remoteAddr;
            } else {
                ip = remoteAddr + "/" + forwarded.split(",")[0];
            }
		} else {
			if(realIp.equals(forwarded)) {
				return realIp;
			} else {
				if(forwarded != null){
                    forwarded = forwarded.split(",")[0];
                }
                ip = realIp + "/" + forwarded;
			}
		}
		return ip;
	}
	
	public abstract boolean saveLog(DataLog log);
}
